package registry

import (
	"encoding/base64"
	"math/rand"
	"net"
	"strconv"
	"time"

	"strings"

	//	"crypto/tls"
	"fmt"

	log "gitee.com/segeln/grit/log"

	//"github.com/liangdas/mqant/log"
	"gitee.com/segeln/grit/registry"
	"gitee.com/segeln/grit/toolkit"
	consul "github.com/hashicorp/consul/api"
	//hash "github.com/mitchellh/hashstructure"
	//consul "github.com/hashicorp/consul/api"
)

/*
type consulRegistry struct {
	Address string
	Client  *consul.Client
	opts    Options

	// connect enabled
	connect bool

	sync.Mutex
	register map[string]uint64
}
*/
type consulRegistry struct {
	address string
	Client  *consul.Client
	//	opts    registry.Options
	//服务类型
	servstyle    string
	connect      bool
	registration *consul.AgentServiceRegistration

	services map[string](map[string]*registry.ServiceInfo)

	//sync.Mutex
	//register map[string]uint64
}

func NewConsulRegistry(address string, servstyle string) registry.Registry {
	cr := &consulRegistry{
		address:   address, //"127.0.0.1:8500",
		servstyle: servstyle,
		//opts: registry.Options{},
		//register: make(map[string]uint64),
	}
	//configure(cr, opts...)
	cr.services = make(map[string](map[string]*registry.ServiceInfo), 0)
	return cr
}

func (this *consulRegistry) Init(s *registry.ServiceInfo) error { //}, opts ...registry.Option) error {
	config := consul.DefaultConfig()
	//confug
	config.Address = this.address //  "127.0.0.1:8500"
	c, err := consul.NewClient(config)
	if err != nil {
		log.Errorln("create consule client error:", err)
	}
	version, err := toolkit.Encrypt(fmt.Sprintf("v:%s", s.Version))
	if err != nil {
		log.Errorln("Encrypt failed,error:", err)
	}
	versionbase64 := base64.StdEncoding.EncodeToString(version)

	tags := make([]string, 0)
	//版本信息
	tags = append(tags, versionbase64) // string(version))
	this.Client = c
	//	checkPort := 8084
	registration := new(consul.AgentServiceRegistration)
	registration.Address = s.Address
	registration.Port = s.Port
	registration.Tags = s.Tags
	registration.ID = fmt.Sprintf("%s_%s", s.Name, s.ID)
	registration.Name = s.Name
	registration.Tags = tags
	checkGrpc := fmt.Sprintf("%s:%d/%s", registration.Address, registration.Port, s.Name)
	fmt.Println("checkGrpc:", checkGrpc)

	registration.Check = &consul.AgentServiceCheck{
		//HTTP:     fmt.Sprintf("http://%s:%d%s", registration.Address, checkPort, "/check"),
		//GRPC:     fmt.Sprintf("%s:%d/%s", registration.Address, registration.Port, s.Name),
		Timeout:  "3s", //超时时间
		Interval: "5s", //健康检查间隔
		//DeregisterCriticalServiceAfter: "30s", //check失败后30秒删除本服务
		DeregisterCriticalServiceAfter: "5s", //check失败后30秒删除本服务
	}
	switch this.servstyle {
	case "http":
		registration.Check.HTTP = fmt.Sprintf("http://%s:%d/check", registration.Address, registration.Port) //, "/check"),
	case "grpc":
		registration.Check.GRPC = fmt.Sprintf("%s:%d/%s", registration.Address, registration.Port, s.Name)
	}
	this.registration = registration
	rand.Seed(time.Now().Unix())
	return nil
}

func (this *consulRegistry) Register() error {
	err := this.Client.Agent().ServiceRegister(this.registration)
	if err != nil {
		log.Errorln("register service (", this.registration.Name, "_", fmt.Sprintf("%s:%d", this.registration.Address, this.registration.Port), ")failed, error:", err)
		return err
	}
	return nil
}

func (this *consulRegistry) Deregister() error {
	log.Infoln("registry deregister,service id :", this.registration.ID)
	err := this.Client.Agent().ServiceDeregister(this.registration.ID)
	if err != nil {
		log.Errorln("Deregister service ", this.registration.ID, " failed, error:", err)
		return err
	}

	return nil
}

func (this *consulRegistry) Monitor(servername string, lastIndex uint64) uint64 {
	fmt.Println("Monitor start...........")
	services, metainfo, err := this.Client.Health().Service(servername, "", true, &consul.QueryOptions{
		WaitIndex: lastIndex, //w.lastIndex, // 同步点，这个调用将一直阻塞，直到有新的更新
	})

	if err != nil {
		log.Errorln("error retrieving instances from Consul: %v", err)
	}
	fmt.Println("Monitor end1...........")

	addrs := map[string]struct{}{}
	for _, service := range services {
		addrs[net.JoinHostPort(service.Service.Address, strconv.Itoa(service.Service.Port))] = struct{}{}
	}
	fmt.Println("Monitor end2...........")
	fmt.Println("Monitor end1...........", metainfo.LastIndex)

	return metainfo.LastIndex

}

func (this *consulRegistry) GetSid() string {
	return this.registration.ID
}

//通服务名称选择可用的服务地址
func (this *consulRegistry) getServices(sername, serversion string) []*registry.ServiceInfo { // ()//[]registry.ServiceInfo, error) {
	listSrv := make([]*registry.ServiceInfo, 0)
	_, mapservices, err := this.Client.Agent().AgentHealthServiceByName(sername)
	if err != nil {
		log.Errorln(" get consule services failed,error:", err)
		return listSrv
	}
	for _, vserver := range mapservices {
		if vserver.Checks.AggregatedStatus() != consul.HealthPassing {
			continue
		}
		v := vserver.Service
		//if v.Service != sername {
		//	continue
		//}
		//当前服务版本号信息
		version := ""
		for _, tag := range v.Tags {
			tagdecode, err := toolkit.DecryptBase64(tag)
			if err != nil {
				log.Errorln("tag 解释错误:", err)
				continue
			}
			if tagdecode != "" {
				tagDecodeArray := strings.Split(tagdecode, ":")
				if len(tagDecodeArray) == 2 {
					switch tagDecodeArray[0] {
					case "v": //当前服务版本号信息
						version = tagDecodeArray[1]
					}
				}
			}
		}
		if serversion != "" && serversion != version {
			continue
		}
		//		serkey := fmt.Sprintf("%s:%d", v.Address, v.Port)

		listSrv = append(listSrv, &registry.ServiceInfo{
			ID:       v.ID,
			Name:     v.Service,
			Port:     v.Port,
			Address:  v.Address,
			Tags:     v.Tags,
			Version:  version,
			Active:   true,
			UseCount: 0,
		})
	}
	return listSrv
}

//通服务名称选择可用的服务地址
func (this *consulRegistry) getServices_old(sername, serversion string) error { // ()//[]registry.ServiceInfo, error) {
	//r := make([]registry.ServiceInfo, 0)
	//getHealthyServiceInstances
	//this.Client.Agent().AgentHealthServiceByName(sername)
	//mapservices, err :=this.Client.Agent().AgentHealthServiceByName(sername)
	mapservices, err := this.Client.Agent().Services()
	if err != nil {
		log.Errorln(" get consule services failed,error:", err)
		return err
	}
	for _, v := range mapservices {
		if v.Service != sername {
			continue
		}
		//	log.Errorln("v:::::", *v)
		//	log.Errorln("v:::::", v.Connect)
		//当前服务版本号信息
		version := ""
		for _, tag := range v.Tags {
			tagdecode, err := toolkit.DecryptBase64(tag)
			if err != nil {
				log.Errorln("errrror:", err)
				continue
			}
			if tagdecode != "" {
				tagDecodeArray := strings.Split(tagdecode, ":")
				if len(tagDecodeArray) == 2 {
					switch tagDecodeArray[0] {
					case "v": //当前服务版本号信息
						version = tagDecodeArray[1]
					}
				}
			}
		}
		if serversion != "" && serversion != version {
			continue
		}
		serkey := fmt.Sprintf("%s:%d", v.Address, v.Port)
		if svalueByName, okByName := this.services[sername]; okByName {
			//serkey := fmt.Sprintf("%s:%d", v.Address, v.Port)

			if svaluebyaddress, okByAddress := svalueByName[serkey]; okByAddress {
				svaluebyaddress.Active = true
			} else {
				sinfo := &registry.ServiceInfo{
					ID:       v.ID,
					Name:     v.Service,
					Port:     v.Port,
					Address:  v.Address,
					Tags:     v.Tags,
					Version:  version,
					Active:   true,
					UseCount: 0,
				}
				this.services[sername][serkey] = sinfo
			}
		} else {
			sinfo := &registry.ServiceInfo{
				ID:       v.ID,
				Name:     v.Service,
				Port:     v.Port,
				Address:  v.Address,
				Tags:     v.Tags,
				Version:  version,
				Active:   true,
				UseCount: 0,
			}
			sinfosByAddress := make(map[string]*registry.ServiceInfo, 0)
			sinfosByAddress[serkey] = sinfo
			this.services[sername] = sinfosByAddress //     = make(map[string]*registry.ServiceInfo, 0)
		}
		/*
		   Active   bool   //是否有效
		   	UseCount int    //客户调用次数
		*/
		//log.Errorln("key:", k, ",service:", v.Address, ",", v.Port, ",", v.ID, ",", v.Service, ",Version:", version)
	}
	return nil
}

func (this *consulRegistry) SelectorService(sername, serversion string) (*registry.ServiceInfo, error) {
	services := this.getServices(sername, serversion)
	serviceNumber := len(services)
	if serviceNumber == 0 {
		//无可用的服务
		return nil, toolkit.Error_System_NoService
	}
	if serviceNumber == 1 {
		return services[0], nil
	}
	index := rand.Intn(serviceNumber)
	//index := rand.Intn(1)
	return services[index], nil

	/*
		key := ""
		usecount := int64(9999999999) //  19223372036854775807
		index := 0
		if svalueByName, okByName := this.services[sername]; okByName {
			for k, v := range svalueByName {
				if v.Active {
					//第一个服务
					if index == 0 {
						usecount = v.UseCount
						key = k
						index = 1
						continue
					}
					if v.UseCount < usecount {
						usecount = v.UseCount
						key = k
					}
				}
			}
			if key == "" {
				//无可用的服务
				return nil, toolkit.Error_System_NoService
			} else {
				this.services[sername][key].UseCount++
				return this.services[sername][key], nil
			}
		} else {
			//无可用的服务
			return nil, toolkit.Error_System_NoService
		}
	*/
}
